﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace fap
{
    /// <summary>
    ///     BarChart.xaml 的交互逻辑
    /// </summary>
    public partial class BarChart
    {
        // Using a DependencyProperty as the backing store for CategoryFieldName.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty XAxisFieldProperty = DependencyProperty.Register("XAxisField", typeof (string), typeof (BarChart), new PropertyMetadata("Name"));

        // Using a DependencyProperty as the backing store for XAxisText.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty XAxisTextProperty = DependencyProperty.Register("XAxisText", typeof (string), typeof (BarChart), new PropertyMetadata(""));

        // Using a DependencyProperty as the backing store for BarWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BarWidthProperty = DependencyProperty.Register("BarWidth", typeof (double), typeof (BarChart), new PropertyMetadata(20d));

        // Using a DependencyProperty as the backing store for ItemsSourc.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof (IEnumerable), typeof (BarChart), new PropertyMetadata(null, ItemsSourcePropertyChangedCallback));

        private readonly Dictionary<string, BarData> _cacheBarSource = new Dictionary<string, BarData>();


        private readonly double bottomMargin = 60;
        private readonly double rightMargin = 10;
        private readonly double topMargin = 40;

        private Color _gridLineColor = Colors.SteelBlue;
        private double _gridLineHorizontalThickness = 0.5;

        private Visibility _legendVisibility = Visibility.Visible;

        private IEnumerable _localDataSource;
        private bool _showValueOnBar = true;
        private double _spaceBetweenBars = 8;

        private Brush _textColor = Brushes.Black;


        private Visibility _titleVisibility = Visibility.Collapsed;
        private string _toolTipText = "{0}";
        private List<ValueField> _valueFieldList = new List<ValueField>();


        private List<Brush> colorList = new List<Brush>();
        private double left;

        public BarChart()
        {
            InitializeComponent();
        }

        /// <summary>
        ///     柱状图之间的空隙
        /// </summary>
        public double SpaceBetweenBars
        {
            get { return _spaceBetweenBars; }
            set { _spaceBetweenBars = value; }
        }

        /// <summary>
        ///     颜色表
        /// </summary>
        public List<Brush> ColorList
        {
            get { return colorList; }
            set { colorList = value; }
        }


        /// <summary>
        ///     数据源
        /// </summary>
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable) GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        /// <summary>
        ///     类别字段名称
        /// </summary>
        public string XAxisField
        {
            get { return (string) GetValue(XAxisFieldProperty); }
            set { SetValue(XAxisFieldProperty, value); }
        }


        /// <summary>
        ///     x轴显示文本
        /// </summary>
        public string XAxisText
        {
            get { return (string) GetValue(XAxisTextProperty); }
            set { SetValue(XAxisTextProperty, value); }
        }


        /// <summary>
        ///     值字段名称列表
        /// </summary>
        public List<ValueField> ValueFieldList
        {
            get { return _valueFieldList; }
            set { _valueFieldList = value; }
        }

        /// <summary>
        ///     文字颜色
        /// </summary>
        public Brush TextColor
        {
            get { return _textColor; }
            set { _textColor = value; }
        }


        /// <summary>
        ///     头部标题
        /// </summary>
        public string Title { get; set; }


        /// <summary>
        ///     是否在柱状图上方显示数值
        /// </summary>
        public bool ShowValueOnBar
        {
            get { return _showValueOnBar; }
            set { _showValueOnBar = value; }
        }

        /// <summary>
        ///     文字提示,例如 ： 【销量：{0}】
        /// </summary>
        public string ToolTipText
        {
            get { return _toolTipText; }
            set { _toolTipText = value; }
        }

        /// <summary>
        ///     柱状图宽度
        /// </summary>
        public double BarWidth
        {
            get { return (double) GetValue(BarWidthProperty); }
            set { SetValue(BarWidthProperty, value); }
        }


        /// <summary>
        ///     显示水平线
        /// </summary>
        public bool ShowHorizontalGridLine { get; set; }

        /// <summary>
        ///     水平线粗细
        /// </summary>
        public double GridLineHorizontalThickness
        {
            get { return _gridLineHorizontalThickness; }
            set { _gridLineHorizontalThickness = value; }
        }

        /// <summary>
        ///     水平线颜色
        /// </summary>
        public Color GridLineColor
        {
            get { return _gridLineColor; }
            set { _gridLineColor = value; }
        }

        /// <summary>
        ///     图例可见度
        /// </summary>
        public Visibility LegendVisibility
        {
            get { return _legendVisibility; }
            set { _legendVisibility = value; }
        }

        /// <summary>
        ///     x轴标签的旋转角度
        /// </summary>
        public double XAxisLabelSkewAngle { get; set; }

        public Visibility TitleVisibility
        {
            get { return _titleVisibility; }
            set { _titleVisibility = value; }
        }


        /// <summary>
        ///     x轴最大值
        /// </summary>
        public double MaxX { get; set; }

        /// <summary>
        ///     y轴最大值
        /// </summary>
        public double MaxY { get; set; }

        /// <summary>
        ///     x轴最小值
        /// </summary>
        public double MinX { get; set; }

        /// <summary>
        ///     y轴最小值
        /// </summary>
        public double MinY { get; set; }

        /// <summary>
        ///     x轴刻度间隔步长
        /// </summary>
        public double XStep { get; set; }

        /// <summary>
        ///     y轴刻度间隔步长
        /// </summary>
        public double YStep { get; set; }

        private static void ItemsSourcePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var chart = d as BarChart;
            if (chart != null && chart.ItemsSource != null)
            {
                chart._localDataSource = chart.ItemsSource;
                chart.MaxY = chart.GetMaxY();
                var collec = chart.ItemsSource as INotifyCollectionChanged;
                if (collec != null)
                {
                    collec.CollectionChanged += delegate
                    {
                        chart._localDataSource = collec as IEnumerable;
                        //获取y最大值 
                        chart.MaxY = chart.GetMaxY();
                        chart.RefreshView();
                    };
                }
            }
        }

        /// <summary>
        ///     刷新视图，刷新视图前需要给y最大值一个数
        /// </summary>
        public void RefreshView()
        {
            try
            {
                if (!IsLoaded || chartGrid.ActualHeight < 100 || chartGrid.ActualWidth < 100 || MaxY < 0)
                {
                    return;
                }
                left = SpaceBetweenBars;
                chartArea.Children.Clear();
                YAxisGrid.Children.Clear();
                if (_localDataSource != null && _localDataSource.Count() > 0)
                {
                    //隐藏“无数据”提示
                    txtNoData.Visibility = Visibility.Hidden;
                    var maxWidth = left + _localDataSource.Count()*(BarWidth + SpaceBetweenBars + (ValueFieldList.Count - 1)*BarWidth + rightMargin);
                    if (maxWidth < chartGrid.ActualWidth)
                    {
                        maxWidth = chartGrid.ActualWidth - LegendsItemsControl.ActualWidth - 30;
                    }
                    chartArea.Width = maxWidth + rightMargin - SystemParameters.VerticalScrollBarWidth;
                    chartArea.Height = chartGrid.ActualHeight - SystemParameters.HorizontalScrollBarHeight;


                    DrawYTicks(MaxY);
                    DrawAxis();
                    foreach (var item in _localDataSource)
                    {
                        //画x轴
                        DrawXAxisLabel(item);
                        for (var i = 0; i < ValueFieldList.Count; i++)
                        {
                            var field = ValueFieldList[i];
                            if (null == field)
                                continue;
                            var fieldValue = item.GetReflectionField(field.FieldName);
                            if (fieldValue == null)
                                continue;

                            double val;
                            double.TryParse(fieldValue.ToString(), out val);
                            var bd = new BarData(chartArea, bottomMargin, topMargin, left, BarWidth, GetBarColor(i), TextColor, ShowValueOnBar);
                            bd.Refresh(val, MaxY);
                            if (!_cacheBarSource.ContainsKey(bd.Id))
                            {
                                _cacheBarSource.Add(bd.Id, bd);
                            }
                            //属性更改事件
                            var notifyObj = item as INotifyPropertyChanged;
                            if (notifyObj != null)
                            {
                                notifyObj.PropertyChanged += (s, e) =>
                                {
                                    if (e.PropertyName == field.FieldName)
                                    {
                                        double newVal;
                                        fieldValue = notifyObj.GetReflectionField(field.FieldName);
                                        double.TryParse(fieldValue.ToString(), out newVal);
                                        if (newVal > 0)
                                        {
                                            bd.Refresh(newVal, MaxY);
                                            var currentMax = _cacheBarSource.Values.Max(_ => _.Value);
                                            //判断y最大值是否改变，如改变则刷新整个视图
                                            if (Math.Abs(currentMax - MaxY) > 0.00001)
                                            {
                                                MaxY = currentMax;
                                                RefreshView();
                                            }
                                        }
                                    }
                                };
                            }
                            //累加左边距
                            left = left + BarWidth;
                            if (i == ValueFieldList.Count - 1)
                            {
                                left = left + SpaceBetweenBars;
                            }
                        }
                    }
                }
                else
                {
                    txtNoData.Visibility = Visibility.Visible;
                }
                DrawLegend();
            }
            catch (Exception)
            {
                chartArea.Children.Clear();
            }
        }


        /// <summary>
        ///     画y轴刻度和文字，并且画水平线
        /// </summary>
        /// <param name="maxY"></param>
        private void DrawYTicks(double maxY)
        {
            YAxisGrid.Margin = new Thickness(0, topMargin, 0, bottomMargin);
            double yTicksCount = 10;


            var yStep = Math.Ceiling(maxY/yTicksCount);


            var step = (float) (yStep/maxY)*(chartArea.Height - bottomMargin - topMargin);

            //从上往下画
            var y1 = chartArea.Height - bottomMargin;

            double yAxisValue = 0;

            for (var i = 0; i <= yTicksCount; i++)
            {
                var marker = new Line {X1 = -4, Y1 = y1};
                marker.X2 = marker.X1 + 4;
                marker.Y2 = y1;
                marker.Stroke = new SolidColorBrush(Colors.Red);
                chartArea.Children.Add(marker);

                //画水平网格
                if (ShowHorizontalGridLine)
                {
                    var gridLine = new Line {X1 = marker.X1, Y1 = y1, X2 = chartArea.Width - rightMargin, Y2 = y1, StrokeThickness = GridLineHorizontalThickness, Stroke = new SolidColorBrush(GridLineColor)};
                    chartArea.Children.Add(gridLine);
                }

                //画y轴字符
                var markText = new TextBlock {Text = yAxisValue.ToString(CultureInfo.CurrentCulture), Foreground = TextColor, FontSize = 10, HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Top, FontWeight = FontWeights.Normal, FontStyle = FontStyles.Normal, TextAlignment = TextAlignment.Right, Margin = new Thickness(5, y1 - 10 - topMargin, 3, 0), Style = null};
                YAxisGrid.Children.Add(markText);
                y1 -= step;
                yAxisValue += yStep;
            }
        }

        /// <summary>
        ///     画x轴和y轴
        /// </summary>
        private void DrawAxis()
        {
            var xaxis = new Line {X1 = 0, Y1 = chartArea.Height - bottomMargin, X2 = chartArea.Width - rightMargin, Y2 = chartArea.Height - bottomMargin, Stroke = new SolidColorBrush(Colors.Gray)};

            chartArea.Children.Add(xaxis);

            var yaxis = new Line {X1 = 0, Y1 = 0, X2 = 0, Y2 = chartArea.Height - bottomMargin - topMargin, Stroke = new SolidColorBrush(Colors.Gray), HorizontalAlignment = HorizontalAlignment.Right};
            YAxisGrid.Children.Add(yaxis);
        }


        /// <summary>
        ///     画图例
        /// </summary>
        private void DrawLegend()
        {
            var legends = new List<Legend>();
            for (var i = 0; i < ValueFieldList.Count; i++)
            {
                var color = GetBarColor(i);
                var fieldName = ValueFieldList[i] == null ? "" : ValueFieldList[i].FieldName;
                legends.Add(new Legend(color, fieldName));
            }
            LegendsItemsControl.ItemsSource = legends;
        }

        private Brush GetBarColor(int index)
        {
            if (index <= ColorList.Count - 1)
            {
                //return ColorList[index];
                return ColorHelper.GetDarkColorByIndex(index);

            }
            return ColorHelper.GetDarkColorByIndex(index);
        }


        /// <summary>
        ///     画x轴标签
        /// </summary>
        private void DrawXAxisLabel(object item)
        {
            var text = item.GetReflectionField(XAxisField) == null ? "" : item.GetReflectionField(XAxisField).ToString();
            var markText = new TextBlock {Text = text, Width = 130, Foreground = TextColor, VerticalAlignment = VerticalAlignment.Top, HorizontalAlignment = HorizontalAlignment.Left, TextAlignment = TextAlignment.Left, FontSize = 15};

            Transform st = new SkewTransform(0, XAxisLabelSkewAngle);
            markText.RenderTransform = st;
            chartArea.Children.Add(markText);
            Canvas.SetTop(markText, chartArea.Height - bottomMargin);
            Canvas.SetLeft(markText, left);
        }


        /// <summary>
        ///     获取y的最大值
        /// </summary>
        /// <returns></returns>
        private double GetMaxY()
        {
            double max = 0;
            foreach (var field in ValueFieldList)
            {
                foreach (var item in _localDataSource)
                {
                    var obj = item.GetReflectionField(field.FieldName);
                    if (obj != null)
                    {
                        double tmp;
                        double.TryParse(obj.ToString(), out tmp);
                        if (tmp > max)
                            max = tmp;
                    }
                }
            }
            return max;
        }

        private void BarChart_OnLoaded(object sender, RoutedEventArgs e)
        {
            RefreshView();
        }

        private void BarChart_OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            RefreshView();
        }
    }

    public class BarData
    {
        private readonly Brush _barColor;
        private readonly double _barWidth;

        private readonly bool _initialized;
        private readonly double _left;

        private readonly double _marginBottom;
        private readonly double _marginTop;
        private readonly bool _showValueOnBar;
        private readonly Brush _txtColor;

        public BarData(Canvas parent, double marginBottom, double marginTop, double left, double barWidth, Brush barColor, Brush txtColor, bool showValueOnBar)
        {
            if (parent == null || parent.Width <= 0)
            {
                return;
            }
            Id = Guid.NewGuid().ToString();
            ParentCanvas = parent;
            _txtColor = txtColor;
            _marginBottom = marginBottom;
            _marginTop = marginTop;
            _left = left;
            _barWidth = barWidth;
            _showValueOnBar = showValueOnBar;
            _barColor = barColor;
            _initialized = true;
        }

        /// <summary>
        ///     当前的值
        /// </summary>
        public double Value { get; set; }

        /// <summary>
        ///     id标识
        /// </summary>
        public string Id { get; set; }


        /// <summary>
        ///     承载柱状图的容器
        /// </summary>
        public Canvas ParentCanvas { get; set; }

        /// <summary>
        ///     柱状图实际
        /// </summary>
        public Rectangle BarRectangle { get; set; }

        /// <summary>
        ///     柱状图提示文字
        /// </summary>
        public TextBlock BarteTextBlock { get; set; }

        public void Refresh(double barCurrentValue, double maxY)
        {
            if (!_initialized)
            {
                return;
            }
            Value = barCurrentValue;
            //计算柱子高度
            var barHeight = (float) barCurrentValue*(ParentCanvas.Height - _marginBottom - _marginTop)/maxY;
            //计算柱子上边距
            var top = ParentCanvas.Height - _marginBottom - barHeight;
            //柱子
            if (BarRectangle == null)
            {
                BarRectangle = new Rectangle {Width = _barWidth, Height = barHeight < 1 ? 1 : barHeight, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top, StrokeThickness = 1, Fill = _barColor, ToolTip = barCurrentValue};

                Canvas.SetTop(BarRectangle, top);
                Canvas.SetLeft(BarRectangle, _left);
                ParentCanvas.Children.Add(BarRectangle);
            }
            else
            {
                BarRectangle.Height = barHeight < 1 ? 1 : barHeight;
                BarRectangle.ToolTip = barCurrentValue;
                Canvas.SetTop(BarRectangle, top);
            }
            //文本 
            var bottom = _marginBottom + barHeight;
            if (_showValueOnBar && BarteTextBlock == null)
            {
                //显示柱子上方文字 
                BarteTextBlock = new TextBlock {Text = barCurrentValue.ToString(CultureInfo.InvariantCulture), Foreground = _txtColor, HorizontalAlignment = HorizontalAlignment.Center, Width = _barWidth, TextAlignment = TextAlignment.Center, FontSize = 10, TextWrapping = TextWrapping.Wrap};
                Canvas.SetBottom(BarteTextBlock, bottom);
                Canvas.SetLeft(BarteTextBlock, _left);
                ParentCanvas.Children.Add(BarteTextBlock);
            }
            else if (_showValueOnBar)
            {
                BarteTextBlock.Text = barCurrentValue.ToString(CultureInfo.InvariantCulture);
                Canvas.SetBottom(BarteTextBlock, bottom);
            }
        }
    }


    /// <summary>
    ///     值字段
    /// </summary>
    public class ValueField
    {
        public string FieldName { get; set; }
    }

    /// <summary>
    ///     颜色辅助类
    /// </summary>
    public static class ColorHelper
    {
        private static readonly List<Brush> DarkColorList = new List<Brush>();

        static ColorHelper()
        {
            DarkColorList.Add(Brushes.Green);
            //DarkColorList.Add(Brushes.MediumBlue);
            DarkColorList.Add(Brushes.Red);
            DarkColorList.Add(Brushes.Tomato);
            DarkColorList.Add(Brushes.Yellow);

            DarkColorList.Add(Brushes.SteelBlue);
            DarkColorList.Add(Brushes.SlateBlue);
            DarkColorList.Add(Brushes.SeaGreen);
            DarkColorList.Add(Brushes.PeachPuff);

            DarkColorList.Add(Brushes.MediumAquamarine);
            DarkColorList.Add(Brushes.MediumOrchid);
            DarkColorList.Add(Brushes.MediumPurple);
            DarkColorList.Add(Brushes.MediumSeaGreen);
            DarkColorList.Add(Brushes.MediumSlateBlue);
            DarkColorList.Add(Brushes.MediumSpringGreen);
            DarkColorList.Add(Brushes.MediumTurquoise);
            DarkColorList.Add(Brushes.MediumVioletRed);

            DarkColorList.Add(Brushes.DarkBlue);
            DarkColorList.Add(Brushes.DarkCyan);
            DarkColorList.Add(Brushes.DarkGoldenrod);
            DarkColorList.Add(Brushes.DarkGray);
            DarkColorList.Add(Brushes.DarkGreen);
            DarkColorList.Add(Brushes.DarkKhaki);
            DarkColorList.Add(Brushes.DarkMagenta);
            DarkColorList.Add(Brushes.DarkOliveGreen);
            DarkColorList.Add(Brushes.DarkOrange);
            DarkColorList.Add(Brushes.DarkOrchid);
            DarkColorList.Add(Brushes.DarkRed);
            DarkColorList.Add(Brushes.DarkSalmon);
            DarkColorList.Add(Brushes.DarkSeaGreen);
            DarkColorList.Add(Brushes.DarkSlateBlue);
            DarkColorList.Add(Brushes.DarkSlateGray);
            DarkColorList.Add(Brushes.DarkTurquoise);
            DarkColorList.Add(Brushes.DarkViolet);
        }

        public static Brush GetDarkColorByIndex(int index)
        {
            if (index >= DarkColorList.Count)
                index = new Random(Environment.TickCount).Next(0, DarkColorList.Count);
            return DarkColorList[index];
        }
    }

    /// <summary>
    ///     柱状图图例
    /// </summary>
    public class Legend
    {
        public Legend()
        {
        }

        public Legend(Brush legendColor, string legendText)
        {
            LegendColor = legendColor;
            LegendText = legendText;
        }

        public Brush LegendColor { get; set; }

        public string LegendText { get; set; }
    }
}